package com.yunhe.abnormal.service.data.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhe.abnormal.client.AuthorityClient;
import com.yunhe.abnormal.client.DeviceClient;
import com.yunhe.abnormal.client.MaintenanceClient;
import com.yunhe.common.constant.*;
import com.yunhe.abnormal.domain.base.AlarmLevel;
import com.yunhe.abnormal.domain.data.AlarmEvent;
import com.yunhe.common.exception.ArgumentErrorException;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.exception.UniqueConstraintsException;
import com.yunhe.abnormal.mapper.data.AlarmEventMapper;
import com.yunhe.abnormal.model.AlarmEventSearchDTO;
import com.yunhe.common.model.PageParam;
import com.yunhe.common.model.authority.FirmDTO;
import com.yunhe.common.model.authority.StationDTO;
import com.yunhe.common.model.authority.UserDTO;
import com.yunhe.common.model.base.AbnormalStatusDTO;
import com.yunhe.common.model.base.StationStatusDTO;
import com.yunhe.common.model.data.AlarmEventDTO;
import com.yunhe.common.model.device.EnergyUnitDTO;
import com.yunhe.common.model.msg.AlarmEventMsgDTO;
import com.yunhe.abnormal.repository.data.AlarmEventQuery;
import com.yunhe.abnormal.repository.data.AlarmEventRepository;
import com.yunhe.abnormal.service.base.AbnormalStatusService;
import com.yunhe.abnormal.service.base.AlarmLevelService;
import com.yunhe.abnormal.service.data.AlarmEventService;
import com.yunhe.abnormal.transaction.MultiTransaction;
import com.yunhe.common.util.*;
import com.yunhe.abnormal.util.ObjectAndByteUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.*;

/**
 * 告警事件Service
 * @author duanxiaoyong
 */
@Service
public class AlarmEventServiceImpl implements AlarmEventService {
    private static final Logger logger = LoggerFactory.getLogger(AlarmEventServiceImpl.class);
    private ObjectMapper mapper = JsonUtil.getObjectMapper();
    @Autowired
    private AlarmEventRepository eventRepository;
    @Autowired
    private AlarmEventQuery alarmEventQuery;
    @Autowired
    private AuthorityClient authorityClient;
    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private AbnormalStatusService abnormalStatusService;
    @Autowired
    private AlarmLevelService alarmLevelService;
    @Autowired
    private MaintenanceClient maintenanceClient;
    @Autowired
    private AlarmEventMapper alarmEventMapper;
    @Autowired
    private HeaderUtil headerUtil;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 事件对象判断处理
     *
     * @param event 事件对象
     */
    private void processBeforeSave(AlarmEvent event)
            throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
//        if (LanguageConsts.TYPE.EN.value().equals(LanguageHolder.getLanguage())) {
//            LanguageHolder.setLanguage(LanguageConsts.TYPE.ZH.value());
//        }
//        //判断参数是否有空
//        Long stationId = event.getStationId();
//        Long devId = event.getDevId();
//        Long alarmStatusId = event.getAlarmId();
//        Long abnormalStatusId = event.getAbnormalStatusId();
//        Long userId = event.getUserIdProcess();
//        if (  stationId == null
//                || devId == null
//                || alarmStatusId == null) {
//            throw new ArgumentErrorException("所传参数不能为空");
//        }
        //判断事件号是否重复（）
//        String eventNum = event.getEventNum();
//        AlarmEventDTO byEventNum = findByEventNum(eventNum);
//        //1.新增的时候判断EventNum是否重复，2.修改的时候判断修改ID和EventNum的ID是否相等
//        if ((event.getId() == null && byEventNum != null) || (event.getId() != null && !event.getId().equals(byEventNum.getId()))) {
//            throw new ArgumentErrorException("事件号为" + event.getEventNum() + "的事件已存在");
//        }
        //设置单位ID和单位标题
//        FirmDTO firm = authorityClient.getFirmById(firmId).getBody().getResults();
//        if (firm == null && firm.getTitle() == null) {
//            throw new ObjectNotFoundException("找不到Id为" + firmId + "的单位");
//        }
        //设置电站ID和电站标题
//        StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
//        if (station != null && station.getTitle() != null) {
//            if (stationId != null && event.getStationTitle() == null) {
//                event.setStationTitle(station.getTitle());
//            }
//        } else if (station == null || station.getTitle() == null) {
//            throw new ObjectNotFoundException("未找到ID为" + stationId + "的电站");
//        }
//        String timeZone = authorityClient.getTimeZoneIdByStationId(stationId).getBody().getResults();
//        if (timeZone == null) {
//            logger.error("电站{}的时区未查询到", stationId);
//            throw new ObjectNotFoundException("该事件所属电站时区未找到");
//        }
//        Long rtime = System.currentTimeMillis();
//        String dtime = DateTimeUtils.getDateString(rtime, timeZone);
        //设置设备ID和设备标题
        //设置设备名称栏属性
//        Long deviceId = event.getDevId();
//        if (deviceId != null && event.getDevTitle() == null) {
//            String label = abnormalRuleService.getLabel(deviceId);
//            if (label.equals("Device")) {
//                String deviceTitle = deviceClient.getDevicePathTitleByDeviceId(deviceId).getBody().getResults();
//                if (deviceTitle != null && deviceTitle != "") {
//                    event.setDevTitle(deviceTitle);
//                } else {
//                    logger.error("未找到ID为{}的设备", deviceId);
//                }
//            } else if (label.equals("EnergyUnit")) {
//                EnergyUnitDTO energyUnit = deviceClient.getEnergyUnitById(deviceId).getBody().getResults();
//                if (energyUnit != null) {
//                    event.setDevTitle(energyUnit.getTitle());
//                } else {
//                    logger.error("未找到ID为{}的能量单元", deviceId);
//                }
//            } else if (label.equals("Station")) {
//                event.setDevTitle(station.getTitle());
//            }
//        }
        //设置设备告警条件ID和标题
//        if (alarmStatusId != null && event.getAlarmTitle() == null) {
//            String ruleLabel = abnormalRuleService.getLabel(alarmStatusId);
//            if (ruleLabel.equals("AlarmStatus")) {
////                AlarmStatusDTO alarmStatus = alarmStatusService.findByAlarmStatusId(alarmStatusId);
////                if (alarmStatus != null && alarmStatus.getTitle() != null) {
////                    event.setAlarmTitle(alarmStatus.getTitle());
////                } else {
////                    throw new ObjectNotFoundException("找不到Id为" + alarmStatusId + "的异常名称");
////                }
//            } else if (ruleLabel.equals("AbnormalRule")) {
//                AbnormalRule abnormalRule = abnormalRuleService.findById(alarmStatusId);
//                if (abnormalRule != null && abnormalRule.getTitle() != null) {
//                    event.setAlarmTitle(abnormalRule.getTitle());
//                } else {
//                    throw new ObjectNotFoundException("找不到Id为" + alarmStatusId + "的异常名称");
//                }
//            }
//        }
        // TODO
        //设置告警类型（事件，告警）ID和标题
//        AbnormalTypeDTO abnormalType = alarmTypeService.findByAlarmTypeId(alarmTypeId);
//        if (alarmTypeId != null && abnormalType.getTitle() != null) {
//            event.setAlarmTypeTitle(abnormalType.getTitle());
//            event.setAlarmTypeName(abnormalType.getName());
//        } else {
//            throw new ObjectNotFoundException("找不到Id为" + alarmTypeId + "的异常类型");
//        }
        //根据新增或者更新调用，编辑异常处理状态
        //若为新增，则设置为待处理
//        if (event.getId() == null) {
//            AbnormalStatusDTO ready = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
//            event.setContinueTime(0L);
//            event.setAbnormalStatusId(ready.getId());
//            event.setAbnormalStatusName(ready.getName());
//            event.setAbnormalStatusTitle(ready.getTitle());
//        } else {
//            //若为更新，则更新持续时间，并更新处理状态
//            AbnormalStatusDTO abnormalStatus = abnormalStatusService.findById(abnormalStatusId);
//            if (abnormalStatus == null) {
//                throw new ObjectNotFoundException("找不到Id为" + abnormalStatusId + "的异常状态");
//            } else {
//                event.setAbnormalStatusTitle(abnormalStatus.getTitle());
//                event.setAbnormalStatusName(abnormalStatus.getName());
//            }
//            //若为已消除，则更新处理时间和持续时间
//            if (abnormalStatus.getName().equals(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName())) {
//                event.setLatestProcessTime(dtime);
//                event.setLatestProcessRtime(rtime);
//                event.setContinueTime(rtime - event.getStartRtime());
//            }
//        }
//        //设置告警等级ID，标题和属性
//        AlarmLevelDTO alarmLevel = null;
//        if (event.getAlarmLevelId() != null){
//            alarmLevel = alarmLevelService.findById(event.getAlarmLevelId());
//        } else if (alarmStatusId != null) {
//            alarmLevel = alarmLevelService.findAlarmLevelByAlarmId(event.getAlarmId());
//        }
//        if (alarmLevel == null) {
//            throw new ObjectNotFoundException("找不到设备告警状态的告警等级");
//        } else {
//            event.setAlarmLevelId(alarmLevel.getId());
//            event.setAlarmLevelName(alarmLevel.getName());
//            event.setAlarmLevelTitle(alarmLevel.getTitle());
//        }
//        //设置处理人ID和标题
//        if (userId != null) {
//            UserDTO user = authorityClient.getUserById(userId).getBody().getResults();
//            if (user == null) {
//                throw new ObjectNotFoundException("找不到Id为" + userId + "的用户");
//            } else {
//                event.setUserTitleProcess(user.getTitle());
//            }
//        } else {
//            event.setUserTitleProcess(SYSTEM);
//        }
//        String startTime = event.getStartTime();
//        if (startTime == null) {
//            event.setStartTime(dtime);
//            event.setStartRtime(rtime);
//        }
    }

    /**
     * 新增事件对象
     * @param event 事件对象
     */
    @MultiTransaction
    private AlarmEventDTO doCreateEvent(AlarmEvent event)
            throws Exception {
        event.setId(null);
        //判断该设备该告警规则是否有未消除的记录
        List<AlarmEvent> byDevAndAlarmStatus = eventRepository.findByDeviceAndAlarmStatus(event.getDevId(),
                Collections.singletonList(event.getAlarmId()));
        if (byDevAndAlarmStatus != null && byDevAndAlarmStatus.size() != 0) {
            return null;
        }
        StationDTO station = authorityClient.getStationById(event.getStationId()).getBody().getResults();
        if (station == null) {
            throw new ObjectNotFoundException("该电站不存在");
        }
        //设置电站ID和电站标题
        if (StringUtil.isEmpty(event.getStationTitle())) {
            event.setStationTitle(station.getTitle());
        }
        //若电站不为空，则判断电站状态判断是否新增
        StationStatusDTO stationStatus = station.getStationStatus();
        //若为调试中，则在记录中添加调试中
        if (AuthorityConsts.StationStatusConstant.DEBUGGING.equals(stationStatus.getName())) {
            event.setRecords(AuthorityConsts.StationStatusConstant.DEBUGGING.title());
        }
        //若为建设中或已停用，则不新增告警
        if (AuthorityConsts.StationStatusConstant.CONSTRUCTING.equals(stationStatus.getName())
                || AuthorityConsts.StationStatusConstant.DEACTIVATED.equals(stationStatus.getName())) {
            return null;
        }
        processBeforeSave(event);
        AlarmEvent newEvent = eventRepository.save(event);
        return alarmEventMapper.to(newEvent);
    }

    /**
     * 新增事件对象
     * @param event 事件对象
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AlarmEventDTO createEvent(AlarmEvent event)
            throws Exception {
        AlarmEventDTO result = doCreateEvent(event);
        // 发送事件更新消息
        if (result != null) {
            sendAlarmRefresh(Collections.singletonList(result), SystemConsts.ACTION.ADD);
        }
        return result;
    }

    /**
     * 批量新增事件对象
     *
     * @param eventList 事件对象列表
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public Iterable<AlarmEventDTO> createEvent(List<AlarmEvent> eventList)
            throws Exception {
        List<AlarmEventDTO> newEventList = new ArrayList<>();
        for (AlarmEvent event : eventList) {
            AlarmEventDTO result = this.doCreateEvent(event);
            if (result != null) {
                newEventList.add(result);
            }
        }
        // 发送事件更新消息
        if (newEventList.size() > 0) {
            sendAlarmRefresh(newEventList, SystemConsts.ACTION.ADD);
        }
        return newEventList;
    }

    /**
     * 根据事件ID删除事件对象
     *
     * @param id 事件ID
     */
    @MultiTransaction
    private void deleteById(Long id) {
        maintenanceClient.deleteByEventId(id);
        eventRepository.deleteById(id);
    }

    /**
     * 根据事件ID删除事件对象
     *
     * @param id 事件ID
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void deleteEventById(Long id) {
        deleteById(id);

        // 发送事件更新消息
        AlarmEventDTO alarmEventDTO = new AlarmEventDTO();
        alarmEventDTO.setId(id);
        sendAlarmRefresh(Collections.singletonList(alarmEventDTO), SystemConsts.ACTION.DELETE);
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void deleteEventById(List<Long> ids) {
        for (Long id : ids) {
            deleteById(id);
        }

        // 发送事件更新消息
        List<AlarmEventDTO> results = new ArrayList<>();
        for (Long id : ids) {
            AlarmEventDTO alarmEventDTO = new AlarmEventDTO();
            alarmEventDTO.setId(id);
            results.add(alarmEventDTO);
        }
        if (results.size() > 0) {
            sendAlarmRefresh(results, SystemConsts.ACTION.DELETE);
        }
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void deleteEventByStationId(Long stationId) {
        List<AlarmEvent> byStationId = eventRepository.findByStationId(stationId);
        if (CollectionUtil.isNotEmpty(byStationId)) {
            for (AlarmEvent alarmEvent : byStationId) {
                deleteById(alarmEvent.getId());
            }
            // 发送事件更新消息
            sendAlarmRefresh(alarmEventMapper.to(byStationId), SystemConsts.ACTION.DELETE);
        }

//        eventRepository.deleteAlarmEventsByStationId(stationId);
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void deleteEventByStationId(Long stationId, List<Date> dtime) throws ArgumentErrorException {
        if (dtime == null || dtime.size() != 2) {
            throw new ArgumentErrorException("必须包含开始时间和结束时间");
        }
        List<AlarmEvent> byStationId = eventRepository.findByStationIdAndDtime(stationId, dtime.get(0), dtime.get(1));
        if (CollectionUtil.isNotEmpty(byStationId)) {
            for (AlarmEvent alarmEvent : byStationId) {
                deleteById(alarmEvent.getId());
            }
            // 发送事件更新消息
            sendAlarmRefresh(alarmEventMapper.to(byStationId), SystemConsts.ACTION.DELETE);
        }
    }

    /**
     * 更新事件（更新全部字段）
     * @param event 事件对象
     */
    @MultiTransaction
    private AlarmEventDTO doUpdateEvent(AlarmEvent event)
            throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        boolean exists = event.getId() != null && eventRepository.existsById(event.getId());
        if (!exists) {
            throw new ObjectNotFoundException("找不到ID为" + event.getId() + "的事件");
        }
        processBeforeSave(event);
        AlarmEvent newEvent = null;
        try {
            newEvent = eventRepository.save(event);
        } catch (DataIntegrityViolationException e) {
            // 因为遇到continueTime超出限值的报错，所以捕获一下该错误，调试用
            logger.error("更新异常报错，event.id为：{}，continueTime值为：{}", event.getId(), event.getContinueTime());
            e.printStackTrace();
        }
        return alarmEventMapper.to(newEvent);
    }

    /**
     * 更新事件（更新全部字段）
     * @param event 事件对象
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AlarmEventDTO updateEvent(AlarmEvent event)
            throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmEventDTO resultDto = doUpdateEvent(event);
        // 发送事件更新消息
        if (resultDto != null) {
            sendAlarmRefresh(Collections.singletonList(resultDto), SystemConsts.ACTION.EDIT);
        }
        return resultDto;
    }

    /**
     * 更新事件（指定字段）
     *
     * @param id                  事件ID
     * @param firmId              单位ID
     * @param firmTitle           单位名称
     * @param stationId           电站ID
     * @param stationTitle        电站名称
     * @param devId               设备ID
     * @param devTitle            设备标题
     * @param alarmStatusId       异常名称ID
     * @param alarmTitle          异常名称名称
     * @param alarmTypeId         告警类型ID
     * @param alarmTypeTitle      告警类型名称
     * @param abnormalStatusId    告警状态ID
     * @param abnormalStatusTitle 告警状态名称
     * @param userIdProcess       处理人ID
     * @param userTitleProcess    处理人姓名
     * @param continueTime        持续时间
     * @param startTime           开始时间
     * @param latestProcessTime   最新处理时间
     */
    @MultiTransaction
    private AlarmEventDTO doUpdateEvent(Long id,
                                     Optional<Long> firmId,
                                     Optional<String> firmTitle,
                                     Optional<Long> stationId,
                                     Optional<String> stationTitle,
                                     Optional<Long> devId,
                                     Optional<String> devTitle,
                                     Optional<Long> alarmStatusId,
                                     Optional<String> alarmTitle,
                                     Optional<Long> alarmTypeId,
                                     Optional<String> alarmTypeTitle,
                                     Optional<Long> abnormalStatusId,
                                     Optional<String> abnormalStatusTitle,
                                     Optional<Long> userIdProcess,
                                     Optional<String> userTitleProcess,
                                     Optional<Long> continueTime,
                                     Optional<String> startTime,
                                     Optional<String> latestProcessTime) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmEvent event = eventRepository.findById(id).orElse(null);
        if (event == null) {
            throw new ObjectNotFoundException("找不到ID为" + id + "的事件");
        }
        String timeZone = authorityClient.getTimeZoneIdByStationId(event.getStationId()).getBody().getResults();
        if (timeZone == null) {
            throw new ObjectNotFoundException("该事件所属电站时区未找到");
        }
        firmId.ifPresent(event::setFirmId);
        stationId.ifPresent(event::setStationId);
        stationTitle.ifPresent(event::setStationTitle);
        devId.ifPresent(event::setDevId);
        devTitle.ifPresent(event::setDevTitle);
        alarmStatusId.ifPresent(event::setAlarmId);
        alarmTitle.ifPresent(event::setAlarmTitle);
        alarmTypeId.ifPresent(event::setAlarmTypeId);
        alarmTypeTitle.ifPresent(event::setAlarmTypeTitle);
        abnormalStatusId.ifPresent(event::setAbnormalStatusId);
        abnormalStatusTitle.ifPresent(event::setAbnormalStatusTitle);
        userIdProcess.ifPresent(event::setUserIdProcess);
        userTitleProcess.ifPresent(event::setUserTitleProcess);
        continueTime.ifPresent(event::setContinueTime);
        if (startTime.isPresent()) {
            Long startRtime = DateTimeUtils.getTimestamp(startTime.get(), timeZone);
            event.setStartTime(startTime.get());
            event.setStartRtime(startRtime);
        }
        if (latestProcessTime.isPresent()) {
            Long processRtime = DateTimeUtils.getTimestamp(latestProcessTime.get(), timeZone);
            event.setLatestProcessTime(latestProcessTime.get());
            event.setLatestProcessRtime(processRtime);
        }
        if (abnormalStatusId.isPresent()) {
            AbnormalStatusDTO abnormalStatus = abnormalStatusService.findById(abnormalStatusId.get());
            event.setAbnormalStatusTitle(abnormalStatus.getTitle());
            event.setAbnormalStatusName(abnormalStatus.getName());
            if (abnormalStatus.getName().equals("Eliminated")) {
                String startDtime = event.getStartTime();
                Long startTimeStamp = DateTimeUtils.getTimestamp(startDtime, timeZone);
                event.setContinueTime(System.currentTimeMillis() - startTimeStamp);
            }
        }
        processBeforeSave(event);
        AlarmEvent newEvent = eventRepository.save(event);
//        abnormalEventService.clearAbnormalEventByAlarmEventId(newEvent);
        return alarmEventMapper.to(newEvent);
    }

    /**
     * 更新事件（指定字段）
     * @param id                  事件ID
     * @param firmId              单位ID
     * @param firmTitle           单位名称
     * @param stationId           电站ID
     * @param stationTitle        电站名称
     * @param devId               设备ID
     * @param devTitle            设备标题
     * @param alarmStatusId       异常名称ID
     * @param alarmTitle          异常名称名称
     * @param alarmTypeId         告警类型ID
     * @param alarmTypeTitle      告警类型名称
     * @param abnormalStatusId    告警状态ID
     * @param abnormalStatusTitle 告警状态名称
     * @param userIdProcess       处理人ID
     * @param userTitleProcess    处理人姓名
     * @param continueTime        持续时间
     * @param startTime           开始时间
     * @param latestProcessTime   最新处理时间
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AlarmEventDTO updateEvent(Long id,
                                     Optional<Long> firmId,
                                     Optional<String> firmTitle,
                                     Optional<Long> stationId,
                                     Optional<String> stationTitle,
                                     Optional<Long> devId,
                                     Optional<String> devTitle,
                                     Optional<Long> alarmStatusId,
                                     Optional<String> alarmTitle,
                                     Optional<Long> alarmTypeId,
                                     Optional<String> alarmTypeTitle,
                                     Optional<Long> abnormalStatusId,
                                     Optional<String> abnormalStatusTitle,
                                     Optional<Long> userIdProcess,
                                     Optional<String> userTitleProcess,
                                     Optional<Long> continueTime,
                                     Optional<String> startTime,
                                     Optional<String> latestProcessTime) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmEventDTO result = doUpdateEvent(id, firmId, firmTitle, stationId, stationTitle, devId, devTitle, alarmStatusId,
                alarmTitle, alarmTypeId, alarmTypeTitle, abnormalStatusId, abnormalStatusTitle, userIdProcess,
                userTitleProcess, continueTime, startTime, latestProcessTime);
        // 发送事件更新消息
        if (result != null) {
            sendAlarmRefresh(Collections.singletonList(result), SystemConsts.ACTION.EDIT);
        }
        return result;
    }

    /**
     * 根据事件ID获得事件
     *
     * @param id 事件ID
     */
    @Override
    public AlarmEventDTO findById(Long id) {
        AlarmEvent event = eventRepository.findById(id).orElse(null);
        return alarmEventMapper.to(event);
    }

    /**
     * 根据条件获得事件对象列表
     *
     * @param devId            设备ID
     * @param alarmTypeId      告警类型Id
     * @param abnormalStatusId 告警状态Id
     */
    @Override
    public Iterable<AlarmEventDTO> findEvents(Long devId,
                                              Long alarmTypeId,
                                              Long abnormalStatusId) {
        List<AlarmEvent> results = (List<AlarmEvent>) eventRepository.findEvents(devId, alarmTypeId, abnormalStatusId);
        return alarmEventMapper.to(results);
    }

    @Override
    public AlarmEventDTO findEvents(Long devId, Long alarmTypeId) {
        AlarmEvent result =  eventRepository.findEvents(devId, alarmTypeId);
        return alarmEventMapper.to(result);
    }

    @Override
    public Iterable<AlarmEventDTO> findAllWarning() {
        List<AlarmEvent> results = (List<AlarmEvent>) eventRepository.findAllWarning();
        return alarmEventMapper.to(results);
    }

    @Override
    public Iterable<AlarmEvent> findAllWarningByFirmAndUser(Optional<Long> firmId, Optional<Long> userId, Optional<List<Long>> stationList, Optional<Boolean> isAll, PageParam pageParam) {
        Long currentFirmId;
        Long currentUserId;
        if (firmId.isPresent()){
            currentFirmId = firmId.get();
        } else {
            currentFirmId = headerUtil.getHeaderFirmId();
        }
        if (userId.isPresent()) {
            currentUserId = userId.get();
        } else  {
            currentUserId = headerUtil.getHeaderUserId();
        }
        List<String> list = new ArrayList<>();
        list.add("-start_time");
        pageParam.setSort(list);
        UserDTO user = authorityClient.getUserById(currentUserId).getBody().getResults();
        FirmDTO firm = authorityClient.getFirmById(currentFirmId).getBody().getResults();
        Iterable<AlarmEvent> results = null;
        if (firm.getFirmType().getTitle().equals(AuthorityConsts.FirmTypeConstant.PLATFORM.getName()) && user.getRole().getName().equals(AuthorityConsts.RoleName.ADMINSTRATOR.getName())) {
            if (pageParam.getPage() != -1 && pageParam.getSize() != -1) {
                if (!isAll.get()) {
                    results = eventRepository.findAllWarningMoreThanModeratePage(QueryUtil.getPageRequest(pageParam));
                } else {
                    results = eventRepository.findAllWarningPage(QueryUtil.getPageRequest(pageParam));
                }
            } else {
                if (!isAll.get()) {
                    results = eventRepository.findAllWarningMoreThanModerate();
                } else {
                    results = eventRepository.findAllWarning();
                }
            }
        } else
            //若不是平台管理员，则查询传参stationList查询相关电站的告警事件
            if (stationList.isPresent()) {
                if (pageParam.getPage() != -1 && pageParam.getSize() != -1) {
                    if (!isAll.get()) {
                        results = eventRepository.findAllWarningMoreThanModeratePage(stationList.get(), QueryUtil.getPageRequest(pageParam));
                    } else {
                        results = eventRepository.findAllWarningPage(stationList.get(), QueryUtil.getPageRequest(pageParam));
                    }
                } else {
                    if (!isAll.get()) {
                        results = eventRepository.findAllWarningMoreThanModerate(stationList.get());
                    } else {
                        results = eventRepository.findAllWarning(stationList.get());
                    }
                }
            }
        return results;
    }



    @Override
    public Iterable<AlarmEventDTO> findAllWarningByFirmId(Long firmId) {
        List<AlarmEvent> results = eventRepository.findByFirmId(firmId);
        return alarmEventMapper.to(results);
    }


    @Override
    public Iterable<AlarmEvent> findWarningByStationId(Long stationId, PageParam pageParam) {
        List<String> list = new ArrayList<>();
        list.add("-start_time");
        pageParam.setSort(list);
        Iterable<AlarmEvent> results = null;
        if (pageParam.getPage() != -1 && pageParam.getSize() != -1) {
            results = eventRepository.findWarningByStationIdPage(stationId, QueryUtil.getPageRequest(pageParam));
        } else {
            results = eventRepository.findWarningByStationId(stationId);
        }
        return results;
    }

//    @Override
//    public Iterable<AlarmEventDTO> findDebuggingEvent() {
//        List<AlarmEvent> results =  eventRepository.findDebuggingEvent();
//        return alarmEventMapper.to(results);
//    }

    /**
     * 根据条件查询事件
     * @param firmId
     * @param stationIds
     * @param devId
     * @param alarmStatusId
     * @param alarmTypeId
     * @param abnormalStatusIds
     * @param alarmLevelIds
     * @param dtime 时间范围
     * @param queryStr 查询参数
     * @param pageParam 分页条件
     * @return
     */
    @Override
    public Iterable<AlarmEvent> findByCondition(Long firmId,
                                                List<Long> stationIds,
                                                Long devId,
                                                Long alarmStatusId,
                                                Long alarmTypeId,
                                                List<Long> abnormalStatusIds,
                                                List<Long> alarmLevelIds,
                                                String dtime,
                                                String queryStr,
                                                PageParam pageParam) {
        if (CollectionUtil.isEmpty(stationIds)) {
            return null;
        }
        Specification spec = (Specification<AlarmEvent>) (root, query, cb) -> {
            List<Predicate> criteria = new ArrayList<>();
            Optional.ofNullable(firmId).ifPresent(value -> criteria.add(cb.equal(root.get("firmId"), value)));
            if (CollectionUtil.isNotEmpty(stationIds)) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("stationId"));
                for (Long stationId : stationIds) {
                    in.value(stationId);
                }
                criteria.add(in);
            }
            Optional.ofNullable(devId).ifPresent(value -> criteria.add(cb.equal(root.get("devId"), value)));
            Optional.ofNullable(alarmStatusId).ifPresent(value -> {
                // TODO
//                AlarmStatusDTO alarmStatus = alarmStatusService.findByAlarmStatusId(value);
//                if (alarmStatus.getCode().equals("-1")) {
//                    criteria.add(cb.notEqual(root.get("alarmStatusId"), "3"));
//                } else {
//                    criteria.add(cb.equal(root.get("alarmStatusId"), value));
//                }
            });
            Optional.ofNullable(alarmTypeId).ifPresent(value -> criteria.add(cb.equal(root.get("alarmTypeId"), value)));
            if (CollectionUtil.isNotEmpty(abnormalStatusIds)) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("abnormalStatusId"));
                for (Long abnormalStatus : abnormalStatusIds) {
                    in.value(abnormalStatus);
                }
                criteria.add(in);
            }
            if (CollectionUtil.isNotEmpty(alarmLevelIds)) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("alarmLevelId"));
                for (Long alarmLevel : alarmLevelIds) {
                    in.value(alarmLevel);
                }
                criteria.add(in);
            }
            Optional.ofNullable(dtime).ifPresent(value -> {
                String[] split = value.split(",");
                List<String> dtimeList = new ArrayList<>();
                Collections.addAll(dtimeList, split);
                Path<String> dtimePath = root.get("startTime");
                if (dtimeList.size() == 1) {
                    String date = dtimeList.get(0);
                    criteria.add(cb.equal(dtimePath, date));
                } else if (dtimeList.size() == 2) {
                    String start = dtimeList.get(0);
                    String end = dtimeList.get(1);
                    criteria.add(cb.between(dtimePath, start, end));
                }
            });
            Optional.ofNullable(queryStr).ifPresent(value -> {
                Predicate p = cb.or(cb.like(root.get("devTitle"), "%" + value.replace("%", "\\%") + "%"),
                        cb.like(root.get("alarmTitle"), "%" + value.replace("%", "\\%") + "%"),
                        cb.like(root.get("records"), "%" + value.replace("%", "\\%") + "%"),
                        cb.like(root.get("stationTitle"), "%" + value.replace("%", "\\%") + "%"));
                criteria.add(p);
            });
            if (criteria.size() == 1) {
                query.where(criteria.get(0));
            } else {
                query.where(criteria.toArray(new Predicate[0]));
            }
            return null;
        };
        Iterable<AlarmEvent> result;
        if (QueryUtil.needPaging(pageParam)) {
            List<String> sortList = pageParam.getSort();
            if (sortList == null) {
                List<String> list = new ArrayList<>();
                list.add("-startTime");
                list.add("abnormalStatusName");
                pageParam.setSort(list);
            } else if (!sortList.contains("abnormalStatusName") && !sortList.contains("-abnormalStatusName")) {
                sortList.add("abnormalStatusName");
                pageParam.setSort(sortList);
            }
            result = eventRepository.findAll(spec, QueryUtil.getPageRequest(pageParam));
        } else {
            Sort sort = pageParam == null ? null : QueryUtil.getSort(pageParam.getSort());
            if (sort != null) {
                result = eventRepository.findAll(spec, sort);
            } else {
                List<String> list = new ArrayList<>();
                list.add("abnormalStatusName");
                list.add("-startTime");
                sort = QueryUtil.getSort(list);
                result = eventRepository.findAll(spec, sort);
            }
        }
        return result;
    }

    /**
     * 查询最后一条事件记录
     */
    @Override
    public AlarmEventDTO findLastEvent() {
        AlarmEvent result =  eventRepository.findLastEvent();
        return alarmEventMapper.to(result);
    }

    /**
     * 根据单位和告警状态查找告警事件
     *
     * @param firmId        单位代码
     * @param alarmStatusId 告警状态代码
     */
    @Override
    public Iterable<AlarmEventDTO> search(Long firmId, Long alarmStatusId) {
        List<AlarmEvent> results = (List<AlarmEvent>) eventRepository.findAlarmEventByAlarmStatusId(firmId, alarmStatusId);
        return alarmEventMapper.to(results);
    }




    /**
     * 获得查询参数
     *
     * @param stationId 电站ID
     */
    private Specification<AlarmEvent> getSpecification(Optional<List<Long>> stationId, Optional<Long> devId, Optional<Long> abnormalStatusId, Optional<Long> alarmLevelId, List<String> dtime) {
        return (root, query, cb) -> {
            List<Predicate> criteria = new ArrayList<>();
            // 电站ID
            if (stationId.isPresent() && CollectionUtil.isNotEmpty(stationId.get())) {
                Path<Object> stationIdPath = root.get("stationId");
                CriteriaBuilder.In<Object> in = cb.in(stationIdPath);
                for (int i = 0; i <stationId.get().size() ; i++) {
                    in.value(stationId.get());
                }
                criteria.add(cb.and(cb.and(in)));
            }
            // 设备id
            if (devId.isPresent()) {
                Path<Long> devIdPath = root.get("devId");
                criteria.add(cb.equal(devIdPath, devId.get()));
            }
            // 告警等级id
            if (alarmLevelId.isPresent()) {
                Path<Long> alarmLevelIdPath = root.get("alarmLevelId");
                criteria.add(cb.equal(alarmLevelIdPath, alarmLevelId.get()));
            }
            // dtime进行判断分割
            if (CollectionUtil.isNotEmpty(dtime)) {
                Path<String> dtimePath = root.get("startTime");
                if (dtime.size() == 1) {
                    String date = dtime.get(0);
                    criteria.add(cb.equal(dtimePath, date));
                } else if (dtime.size() == 2) {
                    String start = dtime.get(0);
                    String end = dtime.get(1);
                    criteria.add(cb.between(dtimePath, start, end));
                }
            }
            //写入查询规则
            if (criteria.size() == 1) {
                query.where(criteria.get(0));
            } else {
                query.where(criteria.toArray(new Predicate[0]));
            }
            return null;
        };
    }

    /**
     * 按天和告警状态查找告警事件数量
     *
     * @param stationId        电站ID
     * @param devId            设备ID
     * @param abnormalStatusId 异常状态ID
     * @param alarmStatusId    告警状态ID
     * @param dtime            时间段
     * @return
     */
    @Override
    public Map<String, Integer> searchDay(Optional<List<Long>> stationId,
                                          Optional<Long> devId,
                                          Optional<Long> abnormalStatusId,
                                          Optional<Long> alarmStatusId,
                                          Optional<Long> alarmLevelId,
                                          List<String> dtime) {
        return alarmEventQuery.getAlarmCountByDate("%Y-%m-%d", stationId.orElse(null), devId.orElse(null),
                abnormalStatusId.orElse(null), alarmLevelId.orElse(null), dtime);
    }

    /**
     * 按月和告警状态查找告警事件数量
     *
     * @param stationId        单位代码
     * @param abnormalStatusId 异常状态ID
     * @param dtime            时间范围
     */
    @Override
    public Map<String, Integer> searchMonth(Optional<List<Long>> stationId,
                                            Optional<Long> devId,
                                            Optional<Long> abnormalStatusId,
                                            Optional<Long> alarmLevelId,
                                            List<String> dtime) {
        return alarmEventQuery.getAlarmCountByDate("%Y-%m", stationId.orElse(null), devId.orElse(null),
                abnormalStatusId.orElse(null), alarmLevelId.orElse(null), dtime);
    }

    @Override
    public Map<String, Integer> searchYear(Optional<List<Long>> stationId, Optional<Long> devId, Optional<Long> abnormalStatusId, Optional<Long> alarmLevelId, List<String> dtime) {
        return alarmEventQuery.getAlarmCountByDate("%Y", stationId.orElse(null), devId.orElse(null),
                abnormalStatusId.orElse(null), alarmLevelId.orElse(null), dtime);
    }

    @Override
    public Map<String, Integer> searchTopDevice(Optional<List<Long>> stationIds,
                                                List<String> dtime,
                                                List<Long> alarmLevelIds) {
        String startTime = dtime.get(0);
        String endTime = dtime.get(1);
        Map<String, Integer> map = new LinkedHashMap<>();
        List<String> devTitles = null;
        if (stationIds.isPresent()) {
            if (CollectionUtil.isEmpty(alarmLevelIds)) {
                devTitles = eventRepository.searchDevTitleByStationIds(stationIds.get(), startTime, endTime);
            }
            else {
                devTitles = eventRepository.searchDevTitleByStationIdsAndAlarmLevel(stationIds.get(), startTime, endTime, alarmLevelIds);
            }
        }
        if (CollectionUtil.isEmpty(devTitles)) {
            return map;
        }
        for (String devTitle : devTitles) {
            if (StringUtil.isEmpty(devTitle)) {
                continue;
            }
            if (map.containsKey(devTitle)) {
                map.put(devTitle, map.get(devTitle) + 1);
            } else {
                map.put(devTitle, 1);
            }
        }
        sortByValue(map);
        return map;
    }

    @Override
    public Map<String, Integer> searchTopAlarmStatus(Optional<List<Long>> stationIds,
                                                     List<String> dtime,
                                                     List<Long> alarmLevelIds) {
        String startTime = dtime.get(0);
        String endTime = dtime.get(1);
        Map<String, Integer> map = new LinkedHashMap<>();
        List<String> abnormalRuleTitles = null;
        if (stationIds.isPresent()) {
            if (CollectionUtil.isEmpty(alarmLevelIds)) {
                abnormalRuleTitles = eventRepository.searchAbnormalRuleTitleByStationIds(stationIds.get(), startTime, endTime);
            }
            else {
                abnormalRuleTitles = eventRepository.searchAbnormalRuleTitleByStationIdsAndAlarmLevel(stationIds.get(), startTime, endTime, alarmLevelIds);
            }
        }
        if (CollectionUtil.isEmpty(abnormalRuleTitles)) {
            return null;
        }
        for (String abnormalRuleTitle : abnormalRuleTitles) {
            if (map.containsKey(abnormalRuleTitle)) {
                map.put(abnormalRuleTitle, map.get(abnormalRuleTitle) + 1);
            } else {
                map.put(abnormalRuleTitle, 1);
            }
        }
        sortByValue(map);
        return map;
    }

    /**
     * 根据单位查找告警事件数量
     *
     * @param stationId 单位代码
     * @param dtime     时间范围
     */
    @Override
    public Map<String, Integer> searchAmount(Long stationId, Optional<Long> alarmLevelId, List<Date> dtime) {
        List<AlarmEvent> list;
        if (alarmLevelId.isPresent()) {
            list = (List<AlarmEvent>) eventRepository.searchAmount(stationId, alarmLevelId.get(), dtime.get(0), dtime.get(1));
        }
        else {
            list = (List<AlarmEvent>) eventRepository.searchAmount(stationId, dtime.get(0), dtime.get(1));
        }
        Map<String, Integer> map = new LinkedHashMap<>();
        for (AlarmEvent AlarmEvent : list) {
            String title = AlarmEvent.getStationTitle();
            if (!StringUtil.isEmpty(title)) {
                if (map.get(title) == null) {
                    map.put(title, 1);
                } else {
                    map.put(title, map.get(title) + 1);
                }
            }
        }
        return sortByValue(map);
    }

    /**
     * 根据起止时间查找电站的告警数量_按能量单元分组
     * @param stationId 电站ID
     * @param dtime 时间范围
     */
    @Override
    public Map<Long, Integer> amountGroupByEnergyUnit(Long stationId, List<Date> dtime) {
        List<AlarmEvent> list = (List<AlarmEvent>) eventRepository.searchAmount(stationId, dtime.get(0), dtime.get(1));
        Map<Long, Integer> result = new LinkedHashMap<>();
        list.forEach(item -> {
            Long deviceId = item.getDevId();
            Long energyUnitId = deviceId;
            EnergyUnitDTO energyUnit = deviceClient.getEnergyUnitByDeviceId(deviceId).getBody().getResults();
            if (energyUnit != null) {
                energyUnitId = energyUnit.getId();
            }
            Integer count = result.computeIfAbsent(energyUnitId, v -> 0);
            result.put(energyUnitId, ++count);
        });
        return result;
    }

    /**
     * 根据map的value排序
     *
     * @param map 需要排序的map
     */
    private <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
        list.sort((o1, o2) -> (o2.getValue()).compareTo(o1.getValue()));
        Map<K, V> result = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
            if (result.size() == 10)
                break;
        }
        return result;
    }

    /**
     * 格式化时间
     *
     * @param time 时间毫秒值
     */
    private String formatTime(Long time) {
        Long hour = time / 3600000;
        if (hour < 24) {
            Long minute = (time - hour * 3600000) / 60000;
            Long second = (time - hour * 3600000 - minute * 60000) / 1000;
            return hour + "时" + minute + "分" + second + "秒";
        } else {
            Long day = time / 86400000;
            hour = (time - day * 86400000) / 3600000;
            Long minute = (time - day * 86400000 - hour * 3600000) / 60000;
            return day + "天" + hour + "时" + minute + "分";
        }
    }

    /**
     * 获取新增或者状态发生改变的事件
     *
     * @param firmId 单位
     */
    @Override
    public List<AlarmEventDTO> searchChange(Long firmId) {
        String AlarmEventListTime = CacheConsts.KEY.ALARM_EVENT_LIST_TIME.value() + firmId;
        String AlarmEventList = CacheConsts.KEY.ALARM_EVENT_LIST.value() + firmId;
        try {
            //redis中获取时间字符串
            if (redisClient.get(AlarmEventListTime) != null) {
                Long t = Long.parseLong(redisClient.get(AlarmEventListTime));
                Long interval = 4500L;
                if (System.currentTimeMillis() - t > interval) {
                    saveChanges(firmId, AlarmEventList, AlarmEventListTime);
                }
            }
            if (redisClient.get(AlarmEventList.getBytes()) == null) {
                saveChanges(firmId, AlarmEventList, AlarmEventListTime);
            }
            List<AlarmEvent> list = (List<AlarmEvent>) ObjectAndByteUtil.toObject(redisClient.get(AlarmEventList.getBytes()));
            List<AlarmEvent> result = new ArrayList<>();
            for (AlarmEvent AlarmEvent : list) {
                if (AlarmEvent.getFirmId().longValue() == firmId) {
                    result.add(AlarmEvent);
                }
            }
            return alarmEventMapper.to(result);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 5s内发生的告警存入redis
     */
    private void saveChanges(Long firmId, String AlarmEventList, String AlarmEventListTime) {
        String oldAlarmEventList = CacheConsts.KEY.OLD_ALARM_EVENT_LIST.value() + firmId;
        List<StationDTO> stations = authorityClient.getStationByFirmId(firmId).getBody().getResults();
        List<Long> stationList = new ArrayList<>();
        for (StationDTO station : stations) {
            stationList.add(station.getId());
        }
        List<AlarmEventDTO> list = (List<AlarmEventDTO>) findAllWarningByFirmId(firmId);
        List<AlarmEventDTO> removeList = new ArrayList<>();
        for (AlarmEventDTO alarmEventDTO : list) {
            if (alarmEventDTO.getAlarmLevelName().equals("slight") || alarmEventDTO.getAlarmLevelName().equals("ignore")) {
                removeList.add(alarmEventDTO);
            }
        }
        list.removeAll(removeList);
        List<AlarmEvent> results = new ArrayList<>();
        List<Long> idList = new ArrayList<>();
        List<AlarmEvent> oldList = new ArrayList<>();
        if (redisClient.get(oldAlarmEventList.getBytes()) != null) {
            oldList = (List<AlarmEvent>) ObjectAndByteUtil.toObject(redisClient.get(oldAlarmEventList.getBytes()));
        }
//        for (AlarmEvent AlarmEvent : list) {
//            for (AlarmEvent event : oldList) {
//                if (AlarmEvent.getId().longValue() == event.getId().longValue()
//                        && AlarmEvent.getabnormalStatusName().longValue() != event.getabnormalStatusName().longValue()) {
//                    results.add(AlarmEvent);
//                }
//            }
//        }
        if (oldList != null) {
            for (AlarmEventDTO AlarmEvent : list) {
                for (AlarmEvent event : oldList) {
                    if (AlarmEvent.getId().equals(event.getId())) {
                        idList.add(AlarmEvent.getId());
                    }
                }
            }
        }
        for (AlarmEventDTO alarmEventDTO : list) {
            if (!idList.contains(alarmEventDTO.getId())) {
                results.add(alarmEventMapper.from(alarmEventDTO));
            }
        }
        redisClient.set(AlarmEventList.getBytes(), ObjectAndByteUtil.toByteArray(results));
        redisClient.set(oldAlarmEventList.getBytes(), ObjectAndByteUtil.toByteArray(list));
        String time = System.currentTimeMillis() + "";
        redisClient.set(AlarmEventListTime, time);
    }

    /**
     * 根据电站代码和告警等级代码获得当日正在告警的事件
     *
     * @param stationId
     * @param alarmLevelId
     * @return
     */
    @Override
    public List<AlarmEventSearchDTO> findWarningDayByStationAndAlarmLevel(Long stationId, Long alarmLevelId) throws ArgumentErrorException {
        StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
        if (station == null) {
            throw new ArgumentErrorException("无法找到该电站");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();
        calendar.set(Calendar.HOUR_OF_DAY, 24);
        Date endTime = calendar.getTime();
        List<AlarmEvent> results = eventRepository.findWarningDayByStationAndAlarmLevel(stationId, alarmLevelId, startTime, endTime);
        return ObjectUtil.do2dto(results, AlarmEventSearchDTO.class);
    }

    @Override
    public List<AlarmEventSearchDTO> findEventDayByStationIdAndTypeName(Long stationId, Long alarmTypeId) throws ArgumentErrorException {
        StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
        if (station == null) {
            throw new ArgumentErrorException("无法找到该电站");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();
        calendar.set(Calendar.HOUR_OF_DAY, 24);
        Date endTime = calendar.getTime();
        List<AlarmEvent> results = eventRepository.findWarningDayByStationAndAlarmType(stationId, alarmTypeId, startTime, endTime);
        return ObjectUtil.do2dto(results, AlarmEventSearchDTO.class);
    }

    @Override
    public Map<String, Integer> realTimeWarningCountByStationIds(List<Long> stationIds) {
        Iterable<AlarmEvent> realTimeResults = eventRepository.findWarningByStationIds(stationIds);
        Map<String, Integer> map = new HashMap<>();
        for (AlarmEvent AlarmEvent : realTimeResults) {
            String alarmLevelName = AlarmEvent.getAlarmLevelName();
            if (!StringUtil.isEmpty(alarmLevelName)) {
                if (map.get(alarmLevelName) == null) {
                    map.put(alarmLevelName, 1);
                } else {
                    map.put(alarmLevelName, map.get(alarmLevelName) + 1);
                }
            }
        }
        return sortByValue(map);
    }

    @Override
    public AlarmLevel getHighestLevelByStationId(Long stationId) {
        List<String> alarmLevels = eventRepository.getStationAlarmLevels(stationId);
        AlarmLevel result = null;
        String[] alarmLevelNames = {AbnormalConsts.ALARM_LEVEL.SERIOUS.value(), AbnormalConsts.ALARM_LEVEL.Moderate.value(),
                AbnormalConsts.ALARM_LEVEL.SLIGHT.value(), AbnormalConsts.ALARM_LEVEL.IGNORE.value()};
        if (alarmLevels != null && alarmLevels.size() > 0) {
            for (String alarmLevelName : alarmLevelNames) {
                if (alarmLevels.contains(alarmLevelName)) {
                    result = alarmLevelService.findByName(alarmLevelName);
                    break;
                }
            }
        }
        return result;
    }

    @Override
    public AlarmLevel getHighestLevelByDeviceIds(Long deviceId) {
        List<String> alarmLevels = eventRepository.getDeviceAlarmLevels(deviceId);
        AlarmLevel result = null;
        if (alarmLevels != null && alarmLevels.size() > 0) {
            if (alarmLevels.contains(AbnormalConsts.ALARM_LEVEL.SERIOUS.value())) {
                result = alarmLevelService.findByName(AbnormalConsts.ALARM_LEVEL.SERIOUS.value());
            } else if (alarmLevels.contains(AbnormalConsts.ALARM_LEVEL.Moderate.value())) {
                result = alarmLevelService.findByName(AbnormalConsts.ALARM_LEVEL.Moderate.value());
            } else if (alarmLevels.contains(AbnormalConsts.ALARM_LEVEL.SLIGHT.value())) {
                result = alarmLevelService.findByName(AbnormalConsts.ALARM_LEVEL.SLIGHT.value());
            } else if (alarmLevels.contains(AbnormalConsts.ALARM_LEVEL.IGNORE.value())) {
                result = alarmLevelService.findByName(AbnormalConsts.ALARM_LEVEL.IGNORE.value());
            }
        }
        return result;
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void clearEventNotEliminated(Map<Long, List<Long>> deviceStatusMap) throws ArgumentErrorException, ObjectNotFoundException,
            UniqueConstraintsException {
        if (deviceStatusMap == null || deviceStatusMap.size() == 0) {
            throw new ArgumentErrorException("设备ID和告警规则ID不能为空");
        }
        AbnormalStatusDTO eliminated = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
        if (eliminated == null) {
            throw new ObjectNotFoundException("已消除的告警状态未找到");
        }
        List<AlarmEventDTO> result = new ArrayList<>();
        for (Map.Entry<Long, List<Long>> entry : deviceStatusMap.entrySet()) {
            Long deviceId = entry.getKey();
            List<Long> alarmStatusIds = entry.getValue();
            List<AlarmEvent> events = eventRepository.findByDeviceAndAlarmStatus(deviceId, alarmStatusIds);
            if (events != null && events.size() != 0) {
                for (AlarmEvent event : events) {
                    event.setAbnormalStatusId(eliminated.getId());
                    event.setUserTitleProcess("System");
                    result.add(doUpdateEvent(event));
                }
            }
        }
        // 发送事件更新消息
        if (CollectionUtil.isNotEmpty(result)) {
            sendAlarmRefresh(result, SystemConsts.ACTION.CLEAR);
        }
    }

    /**
     * 新建历史告警，只需填充字段，不需要其他处理
     * @param alarmEvent
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AlarmEventDTO createForHistory(AlarmEvent alarmEvent) {
        //查找相同规则相同开始时间的记录，若已经生成过，则不生成新的
        AlarmEvent sameOne = eventRepository.findSameRuleAndRtime(alarmEvent.getAlarmId(), alarmEvent.getStartRtime());
        if (sameOne != null) {
            return null;
        }
        AlarmEvent newEvent = eventRepository.save(alarmEvent);
        return alarmEventMapper.to(newEvent);
    }

    /**
     * 历史告警更新
     * @param alarmEvent
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AlarmEventDTO updateForHistory(AlarmEvent alarmEvent) {
        AlarmEvent newEvent = eventRepository.save(alarmEvent);
        return alarmEventMapper.to(newEvent);
    }

    /**
     * 根据告警规则ID和开始时间获得历史最接近开始时间的第一条记录
     * @param ruleId 异常示例ID
     * @param startRtime 开始时间
     */
    @Override
    public AlarmEvent getLastHistoryEventByStartTime(Long ruleId, Long startRtime, Long endRtime) {
        return eventRepository.getLastHistoryEventByStartTime(ruleId, startRtime, endRtime);
    }

    /**
     * 根据告警规则ID和时间范围获得范围内的第一条记录
     * @param ruleId 异常示例ID
     * @param startRtime 开始时间
     * @param endRtime 结束时间
     */
    public AlarmEvent getFirstEventByRuleAndTimeRange(Long ruleId, Long startRtime, Long endRtime) {
        return eventRepository.getFirstEventByRuleAndTimeRange(ruleId, startRtime, endRtime);
    }

    /**
     * 获得待处理的告警事件Map
     */
    @Override
    @Cacheable(value = CacheConsts.Region.ALARM_EVENT_PENDING)
    public Map<String, AlarmEvent> getCurrentEventMap() {
        List<AlarmEvent> alarmEvents = eventRepository.findAllWarningWithoutSort(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
        Map<String, AlarmEvent> result = new HashMap<>();
        for (AlarmEvent alarmEvent : alarmEvents) {
            result.put(String.valueOf(alarmEvent.getAlarmId()), alarmEvent);
        }
        return result;
    }

    /**
     * 发送告警事件更新消息
     * @param alarmEvents 告警列表
     * @param action 事件类型
     */
    private void sendAlarmRefresh(List<AlarmEventDTO> alarmEvents, SystemConsts.ACTION action) {
        AlarmEventMsgDTO msg = new AlarmEventMsgDTO();
        msg.setAction(action.value());
        msg.setAlarmEvents(alarmEvents);
        try {
            kafkaTemplate.send(KafkaConsts.Topic.AlarmEventRefresh, mapper.writeValueAsString(msg));
        } catch (Exception e) {
            logger.error("Kafka发送告警更新消息失败：{}" + alarmEvents.toString());
            e.printStackTrace();
        }
    }

//    @Override
//    @SuppressWarnings("all")
//    public void createEventFromDpu(List<AlarmEventDpuDTO> alarmEventDpuDTOList) {
//        Map<Long, Map<String, Object>> map = new HashMap<>(alarmEventDpuDTOList.size());
//        List<Long> virtualPids = new ArrayList<>();
//        for (AlarmEventDpuDTO alarmEventDpuDTO : alarmEventDpuDTOList) {
//            String content = alarmEventDpuDTO.getContent();
//            Long timestamp = alarmEventDpuDTO.getTimestamp();
//            String[] arr = content.split(",");
//            String pid = arr[0].split(":")[1].replace(" ", "");
//            String value = arr[2].split(":")[1].replace(" ", "");
//            Map<String, Object> item = new HashMap<>(3);
//            item.put("value", Double.parseDouble(value));
//            item.put("timestamp", timestamp);
//            long virtualPid = Long.parseLong(pid);
//            map.put(virtualPid, item);
//            virtualPids.add(virtualPid);
//        }
//        List<AlarmRuleInstanceDTO> alarmRuleInstances = alarmRuleInstanceService.findByVirtualPointIds(virtualPids);
//        List<Long> instanceIds = alarmRuleInstances.stream().map(AlarmRuleInstanceDTO::getId).collect(Collectors.toList());
//        List<AlarmCalculation> alarmCalculations = alarmRuleInstanceService.getCalculationByInstanceIds(instanceIds);
//        Map<Long, AlarmCalculation> calculationMap = alarmCalculations.stream().collect(Collectors.toMap(AlarmCalculation::getId, AlarmCalculation -> AlarmCalculation));
//        List<AlarmNotifyDTO> alarmNotifyList = new ArrayList<>();
//        Map<String, AlarmEvent> readyEventMap = getCurrentEventMap();
//        List<Long> stationIds = new ArrayList<>();
//        Map<Long, Long> stationIdMap = new HashMap<>();
//        for (AlarmRuleInstanceDTO alarmRuleInstance : alarmRuleInstances) {
//            //判断结果值是否需要新增异常
//            Long alarmRuleInstanceId = alarmRuleInstance.getId();
//            String key = String.valueOf(alarmRuleInstanceId);
//            Map<String, Object> valueMap = map.get(alarmRuleInstance.getVirtualPointId());
//            Double result = Double.parseDouble(valueMap.get("value").toString());
//            Long rtime = Long.parseLong(valueMap.get("timestamp").toString());
//            if ((MeasurementConsts.ALARM_STATUS.Alarm.equals(result) && !readyEventMap.containsKey(key))
//                    || (MeasurementConsts.ALARM_STATUS.Normal.equals(result) && readyEventMap.containsKey(key))) {
//                Map<Long, Double> measPnValue = measRealtimeClient.getMeasureRealtime(alarmRuleInstance.getPoints()).getBody().getResults();
//                alarmNotifyList.add(getAlarmNotify(measPnValue, calculationMap.get(alarmRuleInstanceId), rtime, result));
//            }
//            stationIds.add(alarmRuleInstance.getStationId());
//            stationIdMap.put(alarmRuleInstanceId, alarmRuleInstance.getStationId());
//        }
//        Map<Long, StationDTO> stationMap = new HashMap<>(stationIds.size());
//        for (Long stationId : stationIds) {
//            StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
//            stationMap.put(stationId, station);
//        }
//        if (alarmNotifyList.size() > 0) {
//            AbnormalStatusDTO ready = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
//            for (AlarmNotifyDTO alarmNotify : alarmNotifyList) {
//                AlarmCalculation ruleInstance = alarmNotify.getAlarmCalculation();
//
//                SystemConsts.ACTION action = alarmNotify.getAction();
//                Long timeStamp = alarmNotify.getTimeStamp();
//                if (action == SystemConsts.ACTION.ADD) {
//                    //新增异常事件
//                    Long stationId = stationIdMap.get(ruleInstance.getId());
//                    createEvent(ruleInstance, stationMap.get(stationId), ready, timeStamp, true);
//                } else if (action == SystemConsts.ACTION.CLEAR) {
//                    //消除异常事件
//                    AlarmEvent alarmEvent = readyEventMap.get(String.valueOf(ruleInstance.getId()));
//                    if (alarmEvent == null) {
//                        continue;
//                    }
//                    AbnormalStatusDTO eliminated = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
//                    String timeZone = authorityClient.getTimeZoneIdByStationId(alarmEvent.getStationId()).getBody().getResults();
//                    String dtime = DateTimeUtils.getDateString(timeStamp, timeZone);
//                    alarmEvent.setAbnormalStatusId(eliminated.getId());
//                    alarmEvent.setAbnormalStatusName(eliminated.getName());
//                    alarmEvent.setAbnormalStatusTitle(eliminated.getTitle());
//                    alarmEvent.setLatestProcessTime(dtime);
//                    alarmEvent.setLatestProcessRtime(timeStamp);
//                    alarmEvent.setContinueTime(timeStamp - alarmEvent.getStartRtime());
//                    alarmEvent.setUserTitleProcess(AlarmCalculationListener.USER_TITLE_SYSTEM);
//                    try {
//                        updateEvent(alarmEvent);
//                    } catch (Exception e) {
//                        logger.error("消除告警事件报错,{}", e);
//                    }
//                }
//            }
//        }
//    }

//    /**
//     * 生成告警通知对象
//     * @param pnValueMap 点号值
//     * @param ruleInstance 异常实例
//     * @param rtime 时间戳
//     * @param result 计算结果
//     */
//    @Override
//    public AlarmNotifyDTO getAlarmNotify(Map<Long, Double> pnValueMap,
//                                         AlarmCalculation ruleInstance,
//                                         Long rtime, Double result) {
//        AlarmNotifyDTO alarmNotify = new AlarmNotifyDTO();
//        alarmNotify.setPnValueMap(pnValueMap);
//        alarmNotify.setAlarmCalculation(ruleInstance);
//        alarmNotify.setTimeStamp(rtime);
//        if (result == 1) {
//            alarmNotify.setAction(SystemConsts.ACTION.ADD);
//        } else if (result == 0) {
//            alarmNotify.setAction(SystemConsts.ACTION.CLEAR);
//        }
//        return alarmNotify;
//    }
//
//    @Override
//    public AlarmEvent createEvent(AlarmCalculation ruleInstance,
//                                  StationDTO station,
//                                  AbnormalStatusDTO status,
//                                  Long rtime,
//                                  boolean needSave) {
//        //找出设备和电站ID后新增事件
//        DecimalFormat df = new DecimalFormat( "0.00");
//        Long stationId = ruleInstance.getStationId();
//        String timeZone = authorityClient.getTimeZoneIdByStationId(stationId).getBody().getResults();
//        String dtime = DateTimeUtils.getDateString(rtime, timeZone);
//        Boolean chinese = SystemConsts.TIME_ZONE.China.equals(timeZone);
//        List<Long> relatedPointNumber = ruleInstance.getRelatedPoints();
//        AlarmEvent event = new AlarmEvent();
//        event.setAlarmId(ruleInstance.getId());
//        event.setAlarmTitle(ruleInstance.getTypeTitle());
//        event.setStartRtime(rtime);
//        event.setStartTime(dtime);
//        event.setAlarmLevelId(ruleInstance.getLevelId());
//        event.setAlarmLevelName(ruleInstance.getLevelName());
//        event.setAlarmLevelTitle(ruleInstance.getLevelTitle());
//        event.setAlarmTypeId(ruleInstance.getTypeId());
//        event.setAlarmTypeTitle(ruleInstance.getTypeTitle());
//        if (station != null) {
//            event.setStationId(station.getId());
//            event.setStationTitle(station.getTitle());
//        }
//        if (ruleInstance.getRelDeviceId() != null) {
//            event.setDevId(ruleInstance.getRelDeviceId());
//            event.setDevTitle(ruleInstance.getRelDeviceTitle());
//        } else if (ruleInstance.getRelStationId() != null) {
//            event.setDevId(ruleInstance.getRelStationId());
//            event.setDevTitle(ruleInstance.getRelStationTitle());
//        }
//        if (status != null) {
//            event.setAbnormalStatusId(status.getId());
//            event.setAbnormalStatusName(status.getName());
//            event.setAbnormalStatusTitle(status.getTitle());
//            if (AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName().equals(status.getName())) {
//                event.setLatestProcessTime(dtime);
//                event.setLatestProcessRtime(rtime);
//                event.setUserTitleProcess(AlarmCalculationListener.USER_TITLE_SYSTEM);
//            }
//        }
//        List<String> recordList = new ArrayList<>();
//        recordList.add(ruleInstance.getDesc() == null ? "" : ruleInstance.getDesc());
//        if (relatedPointNumber != null && relatedPointNumber.size() != 0) {
//            List<Analog> analogList = deviceClient.getAnalogsByPointNumbers(relatedPointNumber).getBody().getResults();
//            if (analogList != null && analogList.size() != 0)  {
//                Map<Long, Double> analogListValues = redisClient.getMeasurementDouble(relatedPointNumber);
//                for (Analog analog : analogList) {
//                    Long analogPn = analog.getPointNumber();
//                    Double value = analogListValues.get(analogPn);
//                    if (value != null) {
//                        String record;
//                        if (chinese) {
//                            record = analog.getTitle() + "为" + df.format(analogListValues.get(analogPn));
//                        } else {
//                            record = "the value of " + analog.getTitle() + " is " + df.format(analogListValues.get(analogPn));
//                        }
//                        if (analog.getUnit() != null) {
//                            record += analog.getUnit();
//                        }
//                        recordList.add(record);
//                    }
//                }
//            } else {
//                logger.warn("异常规则实例{}未查询到关联点号{}", ruleInstance.getId(), relatedPointNumber);
//            }
//        }
//        event.setRecords(StringUtils.join(recordList.toArray(), ", "));
//        if (needSave) {
//            try {
//                createEvent(event);
//            } catch (Exception e) {
//                logger.error("数据补录新增告警事件报错,{}", e);
//            }
//        }
//        return event;
//    }
}